home *** CD-ROM | disk | FTP | other *** search
- /******************************************************************************
- **
- ** Project Name: DropShell
- ** File Name: DSUserProcs.c
- **
- ** Description: Specific AppleEvent handlers used by the DropBox
- **
- *******************************************************************************
- ** A U T H O R I D E N T I T Y
- *******************************************************************************
- **
- ** Initials Name
- ** -------- -----------------------------------------------
- ** LDR Leonard Rosenthol
- ** MTC Marshall Clow
- ** SCS Stephan Somogyi
- **
- *******************************************************************************
- ** R E V I S I O N H I S T O R Y
- *******************************************************************************
- **
- ** Date Time Author Description
- ** -------- ----- ------ ---------------------------------------------
- ** 06/23/94 LDR Added support for ProcessItem and ProcessFolder handling
- ** 02/20/94 LDR Modified Preflight & Postflight to take item count
- ** 01/25/92 LDR Removed the use of const on the userDataHandle
- ** 12/09/91 LDR Added the new SelectFile userProc
- ** Added the new Install & DisposeUserGlobals procs
- ** Modified PostFlight to only autoquit on odoc, not pdoc
- ** 11/24/91 LDR Added the userProcs for pdoc handler
- ** Cleaned up the placement of braces
- ** Added the passing of a userDataHandle
- ** 10/29/91 SCS Changes for THINK C 5
- ** 10/28/91 LDR Officially renamed DropShell (from QuickShell)
- ** Added a bunch of comments for clarification
- ** 10/06/91 00:02 MTC Converted to MPW C
- ** 04/09/91 00:02 LDR Added to Projector
- **
- ******************************************************************************/
-
- #include <StandardFile.h>
- #include <Timer.h> // For timing
-
- #include <ICTypes.h>
- #include <ICKeys.h>
- #include <ICAPI.h>
-
- #include "DSGlobals.h"
- #include "DSUserProcs.h"
-
- #ifdef __MC68K__
- #include <StuTypes.h>
- #define WideAdd(A,B)
- #define WideSubtract(A,B)
- #endif
-
- // Static Prototypes
- static OSErr ProcessItem(FSSpecPtr myFSSPtr);
- static OSErr ProcessFolder(FSSpecPtr myFSSPtr);
-
-
- /*
- Uncomment this line if you want each item of a dropped folder processed
- as an individual item
- */
- #define qWalkFolders
-
-
- /*
- This routine is called during init time.
-
- It allows you to install more AEVT Handlers beyond the standard four
- */
- #pragma segment Main
- pascal void InstallOtherEvents(void) {
- }
-
-
- /*
- This routine is called when an OAPP event is received.
-
- Currently, all it does is set the gOApped flag, so you know that
- you were called initally with no docs, and therefore you shouldn't
- quit when done processing any following odocs.
- */
- #pragma segment Main
- pascal void OpenApp(void) {
- gOApped = true;
- }
-
-
- /*
- This routine is called when an QUIT event is received.
-
- We simply set the global done flag so that the main event loop can
- gracefully exit. We DO NOT call ExitToShell for two reasons:
- 1) It is a pretty ugly thing to do, but more importantly
- 2) The Apple event manager will get REAL upset!
- */
- #pragma segment Main
- pascal void QuitApp(void) {
- gDone = true; /* All Done! */
- }
-
-
- /*
- This routine is the first one called when an ODOC or PDOC event is received.
-
- In this routine you would place code used to setup structures, etc.
- which would be used in a 'for all docs' situation (like "Archive all
- dropped files")
-
- Obviously, the opening boolean tells you whether you should be opening
- or printing these files based on the type of event recieved.
-
- NEW IN 2.0!
- The itemCount parameter is simply the number of items that were dropped on
- the application and that you will be processing. This gives you the ability
- to do a single preflight for memory allocation needs, rather than doing it
- once for each item as in previous versions.
-
- userDataHandle is a handle that you can create & use to store your own
- data structs. This dataHandle will be passed around to the other
- odoc/pdoc routines so that you can get at your data without using
- globals - just like the new StandardFile.
-
- We also return a boolean to tell the caller if you support this type
- of event. By default, our dropboxes don't support the pdoc, so when
- opening is FALSE, we return FALSE to let the caller send back the
- proper error code to the AEManager.
-
- You will probably want to remove the #pragma unused (currently there to fool the compiler!)
- */
-
- #define DO_TIMING 0
-
- #define IsError(X) ((X) !=1 && (X) != 0)
- #define Alert_NoMemory 130
- #define Alert_NoIC 131
- #define Alert_DiskError 132
-
- #if DO_TIMING
- static UnsignedWide total_start_time, total_end_time;
- static UnsignedWide create_start_time, create_end_time, create_time;
- static UnsignedWide write_start_time, write_end_time, write_time;
- static UnsignedWide close_start_time, close_end_time, close_time;
- static UnsignedWide read_start_time, read_end_time, read_time;
- #endif
-
- typedef struct { HParamBlockRec io; void *GlobalsReg; } ex_HParamBlockRec;
-
- static Handle InBuffer; // Handle to input buffer
- static Handle OutBuffer1, OutBuffer2; // Handles to output output buffers
- static Handle OutBuffer; // Handle to current output buffer
- static long InHandleSize; // Size of input buffer
- static long OutHandleSize; // Size of output buffer
- static short ReadRefNum; // The file being read
- static long BytesRemaining; // Bytes of file still unread
- static Ptr InPtr = NULL; // Next character to read from buffer
- static Ptr InEnd = NULL; // End of data in buffer
- static char *CurrentLine; // Pointer to current line in the inbuffer
- static int LineLength; // Length of current line
- static char TempLineBuffer[256]; // Temp holding buffer for lines spanning buffers
- static Ptr OutPtr; // Next byte to written to buffer
- static Ptr OutEnd; // End output buffer
- static WindowPtr StatusWindow;
- static ICInstance IC_instance = NULL;
- static Handle IC_mappings = NULL;
- static ex_HParamBlockRec Async_Create_PB;
- static HParamBlockRec Async_Info_PB, Async_Open_PB;
- static ParamBlockRec Async_Write_PB, Async_Close_PB;
- static volatile Boolean FileBeingCreated;
- static FSSpec outputSpec, writeSpec, closeSpec;
- static unsigned char output_namesuffix;
- static unsigned char output_namesuffix_length;
- static OSType output_type, output_creator;
- static const Point zeroPoint;
-
- static void MakeStatusWindow(void)
- {
- Rect bounds;
- short screencentre = (qd.screenBits.bounds.right - qd.screenBits.bounds.left) / 2;
- bounds.top = 100;
- bounds.bottom = 290;
- bounds.left = screencentre - 200;
- bounds.right = screencentre + 200;
- StatusWindow = NewWindow(NULL, &bounds, NULL, FALSE, dBoxProc, (WindowPtr)-1, FALSE, 0);
- }
-
- static void PaintStatusWindow(void)
- {
- SetPort(StatusWindow);
- TextSize(12);
- TextFace(bold);
- MoveTo(20, 30); DrawString("\pFast, Simple, Stupid UUDecoder");
- MoveTo(20, 50); DrawString("\pby Stuart Cheshire <cheshire@cs.stanford.edu>");
- MoveTo(20, 70); DrawString("\pThis software is free.");
- MoveTo(20, 90); DrawString("\pYou can do whatever you like with it.");
- TextFace(0);
- MoveTo(20,110); DrawString("\pBuilt using DropShell 2.0");
- MoveTo(20,130); DrawString("\pby Leonard Rosenthol, Marshall Clow, and Stephan Somogyi");
- }
-
- static void UpdateStatusWindowInput(unsigned char *inputname)
- {
- Rect r = StatusWindow->portRect;
- r.top = 135;
- r.bottom = 155;
- r.left = 20;
- EraseRect(&r);
- TextFace(bold);
- MoveTo(20,150); DrawString("\pReading File: "); DrawString(inputname);
- }
-
- static void UpdateStatusWindowOutput(unsigned char *outputname)
- {
- Rect r = StatusWindow->portRect;
- r.top = 155;
- r.left = 20;
- EraseRect(&r);
- TextFace(bold);
- MoveTo(20,170); DrawString("\pWriting File: "); DrawString(outputname);
- }
-
- static void UpdateStatusWindow(Boolean reading)
- {
- Rect r = StatusWindow->portRect;
- r.top = 135;
- r.right = 20;
- EraseRect(&r);
- if (reading) MoveTo(8,150); else MoveTo(8,170);
- TextFace(0);
- DrawString("\p•");
- }
-
- static void StUU_Exit(void)
- {
- if (InBuffer ) { DisposeHandle(InBuffer ); InBuffer = NULL; }
- if (OutBuffer1) { DisposeHandle(OutBuffer1); OutBuffer1 = NULL; }
- if (OutBuffer2) { DisposeHandle(OutBuffer2); OutBuffer2 = NULL; }
- if (StatusWindow) { DisposeWindow(StatusWindow); StatusWindow = NULL; }
- if (IC_instance) { ICStop(IC_instance); IC_instance = NULL; }
- }
-
- static Boolean TrapAvailable(unsigned long trap)
- {
- TrapType tType = (trap & 0x800 ? ToolTrap : OSTrap);
- if (trap & 0x800) // if it is a ToolBox Trap
- {
- unsigned long n = 0x400; // number of toolbox traps
- if (NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(0xAA6E, ToolTrap))
- n = 0x200;
- if ((trap &= 0x7FF) >= n) trap = _Unimplemented;
- }
- return(NGetTrapAddress(trap, tType) != NGetTrapAddress(_Unimplemented, ToolTrap));
- }
-
- #ifdef __MC68K__
-
- // The definition of GetHoldableBytes for 68K code
-
- #if !GENERATINGCFM
- #pragma parameter __D0 GetHoldableBytes()
- #endif
- extern pascal Size GetHoldableBytes(void) TWOWORDINLINE(0x70FD, 0xA05C);
- #else
-
- // The definition of GetHoldableBytes for PPC code
-
- static pascal Size GetHoldableBytes(void)
- {
- static short GetHoldableBytes68K[] =
- {
- 0x70FD, // moveq #-3,d0
- 0xA05C, // MemoryDispatch
- 0x4E75, // rts
- };
- enum
- {
- uppGetHoldableBytesProcInfo = kRegisterBased
- | RESULT_SIZE(kFourByteCode) | REGISTER_RESULT_LOCATION(kRegisterD0)
- };
- return(CallUniversalProc((UniversalProcPtr)GetHoldableBytes68K, uppGetHoldableBytesProcInfo));
- }
-
- #endif
-
- static Size sensibleTempMaxMem(Size *grow)
- {
- Size freemem = TempMaxMem(grow);
- long VMAttr, PhysicalRAM, LogicalRAM;
-
- // if VM on, see if we can revise our estimate of free memory to a more sensible value
- if (TrapAvailable(_Gestalt) &&
- Gestalt(gestaltVMAttr, &VMAttr) == noErr && (VMAttr & (1 << gestaltVMPresent)) &&
- Gestalt(gestaltPhysicalRAMSize, &PhysicalRAM) == noErr &&
- Gestalt(gestaltLogicalRAMSize, &LogicalRAM) == noErr)
- {
- // See how much physical RAM is available
- // If MemoryDispatch is available, use it, else just use
- // a simplistic estimate that half of the physical RAM is holdable
- Size holdable = TrapAvailable(_MemoryDispatch) ? GetHoldableBytes() : PhysicalRAM / 2;
- // See how much memory is in use
- Size usedmem = LogicalRAM - TempFreeMem();
- // See how much physical RAM is free
- Size useable = 0;
- if (holdable > usedmem) useable = holdable - usedmem;
- // If there is very little (or no) physical RAM left, then we'll
- // steal 1/16 of the holdable pages (e.g. 1M on a 16M machine)
- if (useable < holdable/16) useable = holdable/16;
- // If our initial freemem value is more than this amount
- // that we deem sensible to allocate, reduce it
- if (freemem > useable) freemem = useable;
- }
- return(freemem);
- }
-
- #pragma segment Main
- pascal Boolean PreFlightDocs (Boolean opening, short itemCount, Handle *userDataHandle) {
- #pragma unused ( itemCount )
- #pragma unused ( userDataHandle )
- OSErr resultCode1 = noErr, resultCode2 = noErr;
-
- ICError ICerr;
- ICAttr attr;
- Size maxTM, maxHM, growTM = 0, growHM = 0;
-
- if (!opening) return(FALSE); // we support opening, but not printing - see above
-
- MakeStatusWindow();
- ICerr = ICStart(&IC_instance, 'StUU');
- if (!ICerr) ICerr = ICFindConfigFile(IC_instance, 0, nil);
- if (!ICerr) ICerr = ICGetPrefHandle(IC_instance, kICMapping, &attr, &IC_mappings);
- if (ICerr) { (void)Alert(Alert_NoIC, NULL); IC_mappings = NULL; }
-
- // See how much free memory we have in the temporary pool, and in the application heap
- maxTM = sensibleTempMaxMem(&growTM) & ~0xFFF;
- maxHM = MaxMem(&growHM) & ~0xFFF;
-
- // Make sure we keep 32K of application memory in reserve for alerts etc.
- if (maxHM < 0x8000) maxHM = 0; else maxHM -= 0x8000;
-
- // Note: We lock all the handles as soon as possible, otherwise the System has
- // a habit of trying to copy them around to compact the heap, and that absolutely
- // kills performance. If we don't lock the handles, on a machine with VM the
- // launch time can be 10-20 seconds, even using the sensibleTempMaxMem routine.
- if (maxTM > maxHM)
- {
- InHandleSize = maxTM >> 1;
- OutHandleSize = InHandleSize >> 1;
- while (InHandleSize > 0x1000)
- {
- InBuffer = TempNewHandle(InHandleSize, &resultCode1);
- if (resultCode1) { InHandleSize >>= 1; continue; }
- HLock(InBuffer);
- break;
- }
- while (OutHandleSize > 0x1000)
- {
- OutBuffer1 = TempNewHandle(OutHandleSize, &resultCode2);
- if (resultCode2) { OutHandleSize >>= 1; continue; }
- HLock(OutBuffer1);
- OutBuffer2 = TempNewHandle(OutHandleSize, &resultCode2);
- if (resultCode2) { DisposeHandle(OutBuffer1); OutBuffer1 = NULL; OutHandleSize >>= 1; continue; }
- HLock(OutBuffer2);
- break;
- }
- }
- else
- {
- InHandleSize = maxHM >> 1;
- OutHandleSize = InHandleSize >> 1;
- while (InHandleSize > 0x1000)
- {
- InBuffer = NewHandle(InHandleSize);
- if (!InBuffer) { InHandleSize >>= 1; continue; }
- HLock(InBuffer);
- break;
- }
- while (OutHandleSize > 0x1000)
- {
- OutBuffer1 = NewHandle(OutHandleSize);
- if (!OutBuffer1) { InHandleSize >>= 1; continue; }
- HLock(OutBuffer1);
- OutBuffer2 = NewHandle(OutHandleSize);
- if (!OutBuffer2) { DisposeHandle(OutBuffer1); OutBuffer1 = NULL; OutHandleSize >>= 1; continue; }
- HLock(OutBuffer2);
- break;
- }
- resultCode1 = (InBuffer == NULL);
- resultCode2 = (OutBuffer2 == NULL);
- }
- if (resultCode1 || resultCode2) { (void)Alert(Alert_NoMemory, NULL); StUU_Exit(); return(FALSE); }
-
- OutBuffer = OutBuffer1;
- ShowWindow(StatusWindow);
- PaintStatusWindow();
-
- #if DO_TIMING
- Microseconds(&total_start_time);
- create_time.lo = create_time.hi = 0;
- write_time .lo = write_time .hi = 0;
- close_time .lo = close_time .hi = 0;
- read_time .lo = read_time .hi = 0;
- #endif
- return(TRUE);
- }
-
-
- /*
- This routine is called for each file passed in the ODOC event.
-
- In this routine you would place code for processing each file/folder/disk that
- was dropped on top of you.
-
- You will probably want to remove the #pragma unused (currently there to fool the compiler!)
- */
- #pragma segment Main
- pascal void OpenDoc ( FSSpecPtr myFSSPtr, Boolean opening, Handle userDataHandle ) {
- #pragma unused ( myFSSPtr )
- #pragma unused ( opening )
- #pragma unused ( userDataHandle )
- OSErr err = noErr;
-
-
- #ifdef qWalkFolders
- /*
- For this case we need to determine if the FSSpec is a file or folder.
- If it's a folder, we then need to process each item in that folder,
- otherwise just process the item.
- */
- if (FSpIsFolder(myFSSPtr))
- err = ProcessFolder(myFSSPtr);
- else
- err = ProcessItem(myFSSPtr);
- #else
- /*
- For this case we just call ProcessItem on the FSSpec above.
- */
- err = ProcessItem(myFSSPtr);
- #endif
-
- // you should probably do something if you get back an error ;)
- }
-
- // CopyData Benchmarks
- // 82 182 number of bytes (copied 100,000 times)
- // --- ---
- // 145 212 BlockMove
- // 145 212 BlockMoveData
- // 134 283 CopyData
- // 113 237 CopyData in assembler
- // Conclusion: For small blocks, a subroutine is quicker than the BlockMove trap
-
- static void CopyData(register Ptr src, register Ptr dest, register unsigned short len)
- {
- #if THINK_C
- asm {
- sub.w #1, len
- @0 move.b (src)+, (dest)+
- dbra len, @0
- }
- #else
- while(len) { *dest++ = *src++; len--; }
- #endif
- }
-
- static void ShowMsg(unsigned char *msg, unsigned long time)
- {
- unsigned char buffer[128], number[16];
- NumToString(time, number);
- CopyData((Ptr)msg + 1, (Ptr)buffer + 1, msg[0]);
- CopyData((Ptr)number + 1, (Ptr)buffer + 1 + msg[0], number[0]);
- buffer[0] = msg[0] + number[0];
- DebugStr(buffer);
- }
-
- static void ShowWindowMsg(short y, unsigned char *msg, unsigned long time)
- {
- unsigned char buffer[128], number[16];
- NumToString(time, number);
- CopyData((Ptr)msg + 1, (Ptr)buffer + 1, msg[0]);
- CopyData((Ptr)number + 1, (Ptr)buffer + 1 + msg[0], number[0]);
- buffer[0] = msg[0] + number[0];
- MoveTo(20, y); DrawString(buffer);
- }
-
-
- /*
- This routine is the last routine called as part of an ODOC event.
-
- In this routine you would place code to process any structures, etc.
- that you setup in the PreflightDocs routine.
-
- NEW IN 2.0!
- The itemCount parameter was the number of items that you processed.
- It is passed here just in case you need it ;)
-
- If you created a userDataHandle in the PreFlightDocs routines, this is
- the place to dispose of it since the Shell will NOT do it for you!
-
- You will probably want to remove the #pragma unusued (currently there to fool the compiler!)
- */
-
- #pragma segment Main
- pascal void PostFlightDocs ( Boolean opening, short itemCount, Handle userDataHandle ) {
- #pragma unused ( opening )
- #pragma unused ( itemCount )
- #pragma unused ( userDataHandle )
-
- if ( (opening) && (!gOApped) )
- gDone = true; // close everything up!
-
- HUnlock(InBuffer);
- HUnlock(OutBuffer1);
- HUnlock(OutBuffer2);
-
- #if DO_TIMING
- Microseconds(&total_end_time);
- WideSubtract((wide*)&total_end_time, (wide*)&total_start_time);
- EraseRect(&StatusWindow->portRect);
- ShowWindowMsg( 30, "\pRead: ", read_time.hi *1000 + read_time.lo/1000);
- ShowWindowMsg( 50, "\pCreate: ", create_time.hi *1000 + create_time.lo/1000);
- ShowWindowMsg( 70, "\pWrite: ", write_time.hi *1000 + write_time.lo/1000);
- ShowWindowMsg( 90, "\pClose: ", close_time.hi *1000 + close_time.lo/1000);
- ShowWindowMsg(110, "\pTotal: ", total_end_time.hi*1000 + total_end_time.lo/1000);
- while (!Button()) continue;
- #endif
-
- StUU_Exit();
-
- /*
- The reason we do not auto quit is based on a recommendation in the
- Apple event Registry which specifically states that you should NOT
- quit on a 'pdoc' as the Finder will send you a 'quit' when it is
- ready for you to do so.
- */
- }
-
- // *********************************************************************************
- // Main StUU Code body starts here
-
- // I treat any non-printing ascii character as a line break (non-printing ascii
- // characters except for CR and/or NL have no business being in UUencoded data anyway)
- #define IS_LINE_BREAK(C) ((unsigned char)(C) < (unsigned char)32)
-
- static Ptr ReadNextLine(void)
- {
- register Ptr theData;
- register Ptr InPtrCopy = InPtr;
- register Ptr InPtrLimit = InPtrCopy + 255;
-
- // If data is all contiguous in memory use fast optimistic case
- if (InPtrLimit < InEnd)
- {
- if (IS_LINE_BREAK(*InPtrCopy)) InPtrCopy++;
- theData = InPtrCopy;
- while (InPtrCopy < InPtrLimit && !IS_LINE_BREAK(*InPtrCopy)) InPtrCopy++;
- if (InPtrCopy > theData) // If we read a non-blank line
- {
- LineLength = InPtrCopy - theData; // Set the length
- *InPtrCopy++ = 0; // Terminate the line
- InPtr = InPtrCopy; // Update the pointer
- return(theData); // and return
- }
- }
-
- // If the optimistic case fails, try the slow mechanism
- InPtrLimit = InEnd;
- theData = TempLineBuffer;
-
- while (1)
- {
- while (InPtrCopy < InPtrLimit && theData < &TempLineBuffer[255] &&
- !IS_LINE_BREAK(*InPtrCopy)) *theData++ = *InPtrCopy++;
-
- if (InPtrCopy < InPtrLimit) // We haven't run out of data yet
- {
- InPtrCopy++; // Skip over the newline
- // Test to see if the line is has some contents (empty lines are ignored)
- if (theData > TempLineBuffer) break;
- }
- else // We broke out of loop because this chunk is over, so read some more
- {
- OSErr err;
- long inOutCount = BytesRemaining;
- if (inOutCount > InHandleSize) inOutCount = InHandleSize;
- if (inOutCount == 0) break; // If no more to read, stop
- #if DO_TIMING
- Microseconds(&read_start_time);
- #endif
- UpdateStatusWindow(TRUE);
- err = FSRead(ReadRefNum, &inOutCount, *InBuffer);
- UpdateStatusWindow(FALSE);
- #if DO_TIMING
- Microseconds(&read_end_time);
- WideSubtract((wide*)&read_end_time, (wide*)&read_start_time);
- WideAdd((wide*)&read_time, (wide*)&read_end_time);
- #endif
- if (err) break; // If error reading, stop
- InPtrCopy = *InBuffer;
- InPtrLimit = InPtrCopy + inOutCount;
- BytesRemaining -= inOutCount;
- if (inOutCount == 0) { DebugStr("\pError: FSRead read nothing"); break; }
- }
- }
-
- LineLength = theData - TempLineBuffer; // Set the length
- *theData = 0; // Terminate the line
- InPtr = InPtrCopy; // Update the pointers
- InEnd = InPtrLimit;
- return(TempLineBuffer);
- }
-
- static void GetFileType(const unsigned char *name, OSType *t, OSType *c)
- {
- ICMapEntry entry;
- ICError ICerr = -1;
- *t = *c = '????';
- if (IC_mappings) ICerr = ICMapEntriesFilename(IC_instance, IC_mappings, name, &entry);
- if (ICerr == noErr) { *t = entry.file_type; *c = entry.file_creator; }
- }
-
- static void SetNextName(void)
- {
- outputSpec.name[output_namesuffix_length ] = output_namesuffix++;
- outputSpec.name[0] = output_namesuffix_length;
- outputSpec.name[output_namesuffix_length-1] = '.';
- if (output_namesuffix == '9'+1) output_namesuffix = 'A';
- }
-
- static pascal void CreateComplete(void)
- {
- #ifdef __MC68K__
- ex_HParamBlockRec *pb = GetRegisterA0();
- void *oldGlobals = GetGlobalsRegister();
- SetGlobalsRegister(pb->GlobalsReg);
- #endif
- if (Async_Create_PB.io.ioParam.ioResult == dupFNErr) // Duplicate name; try again
- { SetNextName(); PBHCreateAsync(&Async_Create_PB.io); }
- else FileBeingCreated = FALSE;
- #ifdef __MC68K__
- SetGlobalsRegister(oldGlobals);
- #endif
- }
-
- #if GENERATINGCFM
- static RoutineDescriptor CreateCompleteRD = BUILD_ROUTINE_DESCRIPTOR(uppIOCompletionProcInfo, CreateComplete);
- #else
- #define CreateCompleteRD CreateComplete
- #endif
-
- static OSErr StUU_Create(FSSpecPtr sourceFSSPtr, unsigned char *fileName)
- {
- OSErr err = noErr;
- unsigned long the_time;
-
- #if DO_TIMING
- Microseconds(&create_start_time);
- #endif
-
- // Set up new FSSpec
- outputSpec.vRefNum = sourceFSSPtr->vRefNum;
- outputSpec.parID = sourceFSSPtr->parID;
- CopyData((Ptr)fileName, (Ptr)outputSpec.name, 1+fileName[0]);
-
- // Prepare the name suffix, in case we need it because of duplicate file names
- output_namesuffix = '1';
- output_namesuffix_length = fileName[0]+2;
- if (output_namesuffix_length > 31) output_namesuffix_length = 31;
-
- // Fire off the asynchronous file creation
- FileBeingCreated = TRUE;
- Async_Create_PB.io.ioParam.ioCompletion = &CreateCompleteRD;
- Async_Create_PB.io.ioParam.ioResult = noErr;
- Async_Create_PB.io.ioParam.ioNamePtr = outputSpec.name;
- Async_Create_PB.io.ioParam.ioVRefNum = outputSpec.vRefNum;
- Async_Create_PB.io.fileParam.ioDirID = outputSpec.parID;
- #ifdef __MC68K__
- Async_Create_PB.GlobalsReg = GetGlobalsRegister();
- #endif
- err = PBHCreateAsync(&Async_Create_PB.io);
- if (err) FileBeingCreated = FALSE;
-
- // Ask Internet Config to tell us the type and creator codes for this file
- GetFileType(fileName, &output_type, &output_creator);
-
- // Wait for file creation to complete
- while (FileBeingCreated) continue;
- if (Async_Create_PB.io.ioParam.ioResult) err = Async_Create_PB.io.ioParam.ioResult;
-
- if (!err)
- {
- // Set the creator and type
- GetDateTime(&the_time);
- Async_Info_PB.fileParam.ioCompletion = NULL;
- Async_Info_PB.fileParam.ioNamePtr = outputSpec.name;
- Async_Info_PB.fileParam.ioVRefNum = outputSpec.vRefNum;
- Async_Info_PB.fileParam.ioDirID = outputSpec.parID;
- Async_Info_PB.fileParam.ioFlCrDat = the_time;
- Async_Info_PB.fileParam.ioFlMdDat = the_time;
- Async_Info_PB.fileParam.ioFlFndrInfo.fdType = output_type;
- Async_Info_PB.fileParam.ioFlFndrInfo.fdCreator = output_creator;
- Async_Info_PB.fileParam.ioFlFndrInfo.fdFlags = 0;
- Async_Info_PB.fileParam.ioFlFndrInfo.fdLocation = zeroPoint;
- Async_Info_PB.fileParam.ioFlFndrInfo.fdFldr = 0;
- err = PBHSetFInfoAsync(&Async_Info_PB);
- }
-
- if (!err)
- {
- // Open the file
- Async_Open_PB.fileParam.ioCompletion = NULL;
- Async_Open_PB.fileParam.ioNamePtr = outputSpec.name;
- Async_Open_PB.fileParam.ioVRefNum = outputSpec.vRefNum;
- Async_Open_PB.fileParam.ioDirID = outputSpec.parID;
- Async_Open_PB.ioParam.ioPermssn = fsWrPerm;
- err = PBHOpenDFAsync(&Async_Open_PB);
- }
-
- #if DO_TIMING
- Microseconds(&create_end_time);
- WideSubtract((wide*)&create_end_time, (wide*)&create_start_time);
- WideAdd((wide*)&create_time, (wide*)&create_end_time);
- #endif
-
- if (err) { FSpDelete(&outputSpec); ErrorAlert(kErrStringID, kCantCreateErr, err); }
- else UpdateStatusWindowOutput(outputSpec.name);
-
- return(err);
- }
-
- static void ReportErrorAndDelete(void)
- {
- GrafPtr savePort;
- GetPort(&savePort);
- FSpDelete(&closeSpec);
- ParamText(closeSpec.name, NULL, NULL, NULL);
- (void)Alert(Alert_DiskError, NULL);
- SetPort(savePort);
- }
-
- static OSErr WriteChunk(void)
- {
- OSErr err;
- #if DO_TIMING
- Microseconds(&write_start_time);
- #endif
- while (Async_Open_PB.ioParam.ioResult == 1) continue; // Make sure open is complete
- while (Async_Write_PB.ioParam.ioResult == 1) continue; // Make sure previous write is complete
-
- // If the previous write failed, report it now
- if (Async_Write_PB.ioParam.ioResult) return(Async_Write_PB.ioParam.ioResult);
-
- // If our file opened correctly, write to it
- if (Async_Write_PB.ioParam.ioResult == noErr && Async_Open_PB.ioParam.ioResult == noErr)
- {
- writeSpec = outputSpec;
- Async_Write_PB.ioParam.ioCompletion = NULL;
- Async_Write_PB.ioParam.ioResult = noErr;
- Async_Write_PB.ioParam.ioRefNum = Async_Open_PB.ioParam.ioRefNum;
- Async_Write_PB.ioParam.ioBuffer = *OutBuffer;
- Async_Write_PB.ioParam.ioReqCount = OutPtr - *OutBuffer;
- Async_Write_PB.ioParam.ioPosMode = fsAtMark;
- Async_Write_PB.ioParam.ioPosOffset = 0;
- err = PBWriteAsync(&Async_Write_PB);
- if (err) { Async_Write_PB.ioParam.ioResult = err; err = noErr; }
- if (OutBuffer == OutBuffer1) OutBuffer = OutBuffer2; else OutBuffer = OutBuffer1;
- }
- OutPtr = *OutBuffer;
- OutEnd = *OutBuffer + OutHandleSize;
- #if DO_TIMING
- Microseconds(&write_end_time);
- WideSubtract((wide*)&write_end_time, (wide*)&write_start_time);
- WideAdd((wide*)&write_time, (wide*)&write_end_time);
- #endif
- return(err);
- }
-
- // Note, made the decode table unsigned instead of signed to prevent the C compiler
- // from trying to sign-extend the bytes every time it ORs them into a long
- static unsigned char zero_char;
- static unsigned char decode_table[256];
- #define DEC(c) (decode_table[(unsigned char)(c)]) /* single character decode */
-
- static void reset_default_table(void)
- {
- int i;
- for (i= 0; i< 32; i++) decode_table[i] = 0;
- for (i=32; i< 96; i++) decode_table[i] = i - 32;
- for (i=96; i<256; i++) decode_table[i] = 0;
- decode_table['`'] = decode_table[' '];
- decode_table['~'] = decode_table['^'];
- zero_char = ' ';
- }
-
- static void read_translation_table(void)
- {
- unsigned char val = 0;
- int i;
- for (i=0; i<256; i++) decode_table[i] = 0;
- while (CurrentLine[0])
- {
- unsigned char *p;
- CurrentLine = ReadNextLine();
- p = (unsigned char *)CurrentLine;
- while (p[LineLength - 1] == ' ') p[--LineLength] = 0;
- if (val == 0) zero_char = *p;
- while (*p) decode_table[*p++] = val++;
- if (val >= 64) return;
- }
- }
-
- /* Old Think C inline assembly chunk
- #if THINK_C
- asm {
- clr.l d0
- move.b (p)+, d0 ; read first byte
- move.b (decoder,d0), val ; move translated value into val
- move.b (p)+, d0 ; read second byte
- lsl.l #6, val ; shift val left six bits
- or.b (decoder,d0), val ; OR translated value into val
- move.b (p)+, d0 ; read third byte
- lsl.l #6, val ; shift val left six bits
- or.b (decoder,d0), val ; OR translated value into val
- move.b (p)+, d0 ; read final byte
- lsl.l #6, val ; shift val left six bits
- or.b (decoder,d0), val ; OR translated value into val
- }
- */
-
- static OSErr DecodeLine(register Ptr p)
- {
- register Ptr out;
- register unsigned char *decoder = decode_table;
- register unsigned long val;
- register int linelen = decoder[(unsigned char)(*p++)];
- if (OutEnd - OutPtr < 256) { OSErr err = WriteChunk(); if (err) return(err); }
- out = OutPtr; // Careful! Don't set this till *after* WriteChunk
-
- while (linelen > 0)
- {
- val = decoder[(unsigned char)(p[0])]; val <<= 6;
- val |= decoder[(unsigned char)(p[1])]; val <<= 6;
- val |= decoder[(unsigned char)(p[2])]; val <<= 6;
- val |= decoder[(unsigned char)(p[3])];
- out[2] = val; val >>= 8;
- out[1] = val; val >>= 8;
- out[0] = val;
- p += 4;
- out += 3;
- linelen -= 3;
- }
- // Because we output blocks of three, we may overshoot the correct end of line
- // In this case linelen will be negative and we backtrack OutPtr by that amount.
- OutPtr = out + linelen;
- return(noErr);
- }
-
- #define MAX_STASHED_LINES 8
- #define MAX_STASHED_LINE_LENGTH 88
- typedef struct { char c[MAX_STASHED_LINE_LENGTH]; } TextLine;
-
- #define PERFECT_LINE (CurrentLine[0] == 'M' && LineLength >= 61 && LineLength <= 63)
-
- // sourceFSSPtr is the uuencoded file being decoded
- // hp points to the "begin 644 filename.foo" header line
- static OSErr DecodeFile(FSSpecPtr sourceFSSPtr, Ptr hp)
- {
- OSErr err, closeErr;
- TextLine LastPerfectLine;
- TextLine LineStash[MAX_STASHED_LINES];
- int num_stashed_lines = 0;
- Boolean GotPerfectLine = FALSE;
- int i;
- Ptr filemode;
- unsigned char filename[32];
- filename[0] = 0;
- hp += 6; // Skip the "begin "
- while (*hp == ' ') hp++; // Skip the spaces
- filemode = hp; // The Unix file permissions
- while (*hp && *hp != ' ') hp++; // Read the permission portion
- if (*hp) *hp++ = 0; // Terminate the permission string
- while (*hp == ' ') hp++; // Skip the spaces
- while (*hp && *hp != ' ' && filename[0] < sizeof(filename) - 1)
- filename[++filename[0]] = (unsigned char) *hp++;
-
- CurrentLine = ReadNextLine(); // Read the first line of encoded data
- if (!filename[0]) { ErrorAlert(kErrStringID, kNoFileNameErr, 0); return(noErr); }
-
- // Create output file in the same directory
- err = StUU_Create(sourceFSSPtr, filename);
- if (err) return(err);
- // The file is opened asynchronously, so the open proceeds while we start decoding
-
- OutPtr = *OutBuffer; // Set up output pointers
- OutEnd = *OutBuffer + OutHandleSize;
-
- while (CurrentLine[0] && !err)
- {
- // "Good Loop": Read as many good lines as possible
- while (PERFECT_LINE && !err)
- { err = DecodeLine(CurrentLine); CurrentLine = ReadNextLine(); }
-
- GotPerfectLine = FALSE;
- // "Bad Loop": Skip past the bad or questionable lines
- while (CurrentLine[0] && !err)
- {
- int origlen = DEC(CurrentLine[0]); // Original length
- int codedlength = 1 + (origlen * 4 + 2) / 3; // UUencoded length
-
- if (CurrentLine[0] == 'e' && // check for "end" marker
- CurrentLine[1] == 'n' &&
- CurrentLine[2] == 'd') goto end_of_file;
-
- if (CurrentLine[0] == 'b' && // check for another "begin" marker
- CurrentLine[1] == 'e' &&
- CurrentLine[2] == 'g' &&
- CurrentLine[3] == 'i' &&
- CurrentLine[4] == 'n' &&
- CurrentLine[5] == ' ') goto end_of_file;
-
- if (CurrentLine[0] == 't' && // check for another "table" marker
- CurrentLine[1] == 'a' &&
- CurrentLine[2] == 'b' &&
- CurrentLine[3] == 'l' &&
- CurrentLine[4] == 'e') goto end_of_file;
-
- // If line too short, restore truncated trailing spaces
- if (LineLength < codedlength)
- {
- // Mustn't try to expand line in place!
- if (CurrentLine != TempLineBuffer)
- {
- CopyData(CurrentLine, TempLineBuffer, LineLength+1);
- CurrentLine = TempLineBuffer;
- }
- while (LineLength < codedlength) CurrentLine[LineLength++] = zero_char;
- CurrentLine[LineLength] = 0; // Terminate the line
- }
-
- // If we get one perfect line stash it, and set the flag.
- // If we get a second perfect line, assume we are back in body text,
- // and go back to the good loop
- if (PERFECT_LINE)
- {
- if (GotPerfectLine) { err = DecodeLine(LastPerfectLine.c); break; }
- num_stashed_lines = 0;
- GotPerfectLine = TRUE;
- CopyData(CurrentLine, LastPerfectLine.c, LineLength);
- }
- else
- {
- // Stash runt lines in case they turn out to be
- // the last few lines before the "end" marker
- GotPerfectLine = FALSE;
- if (LineLength <= MAX_STASHED_LINE_LENGTH &&
- num_stashed_lines < MAX_STASHED_LINES &&
- origlen > 0 && origlen <= 45 && LineLength <= codedlength + 3)
- CopyData(CurrentLine, LineStash[num_stashed_lines++].c, LineLength);
- }
- CurrentLine = ReadNextLine(); // Read the next line
- }
- }
- end_of_file:
- // Decode any stashed lines
- if (!err && GotPerfectLine) err = DecodeLine(LastPerfectLine.c);
- for (i=0; !err && i<num_stashed_lines; i++) err = DecodeLine(LineStash[i].c);
-
- // Make sure open is complete
- while (Async_Open_PB.ioParam.ioResult == 1) continue;
-
- // If file failed to be opened, give the error message
- if (Async_Open_PB.ioParam.ioResult)
- {
- FSpDelete(&outputSpec);
- ErrorAlert(kErrStringID, kCantCreateErr, Async_Open_PB.ioParam.ioResult);
- return(Async_Open_PB.ioParam.ioResult);
- }
-
- // Write the data out to disk
- if (!err && OutPtr > *OutBuffer) err = WriteChunk();
- // The file is written asynchronously, so the write proceeds while we continue
- // decoding the next file
-
- // And close the file
- #if DO_TIMING
- Microseconds(&close_start_time);
- #endif
- while (Async_Close_PB.ioParam.ioResult == 1) continue; // Make sure previous close is complete
- if (Async_Close_PB.ioParam.ioResult) ReportErrorAndDelete();
- // If the previous close failed, report it now
-
- closeSpec = outputSpec;
- Async_Close_PB.ioParam.ioCompletion = NULL;
- Async_Close_PB.ioParam.ioResult = noErr;
- Async_Close_PB.ioParam.ioRefNum = Async_Open_PB.ioParam.ioRefNum;
- closeErr = PBCloseAsync(&Async_Close_PB);
- if (closeErr) Async_Close_PB.ioParam.ioResult = closeErr;
- // The file is closed asynchronously, so the close proceeds while we continue
- // decoding the next file
-
- #if DO_TIMING
- Microseconds(&close_end_time);
- WideSubtract((wide*)&close_end_time, (wide*)&close_start_time);
- WideAdd((wide*)&close_time, (wide*)&close_end_time);
- #endif
-
- if (err) ReportErrorAndDelete();
-
- return(err);
- }
-
- // *********************************************************************************
-
- /*
- This routine gets called for each item (which could be either a file or a folder)
- that the caller wants dropped. The determining factor is the definition of the
- qWalkFolder compiler directive. Either way, the item in question should be
- processed as a single item and not "dissected" into component units (like subfiles
- of a folder!)
- */
- static OSErr ProcessItem(FSSpecPtr myFSSPtr)
- {
- OSErr err = noErr;
-
- err = FSpOpenDF(myFSSPtr, fsRdPerm, &ReadRefNum);
- if (err) return(err);
-
- err = GetEOF(ReadRefNum, &BytesRemaining);
- if (err) { FSClose(ReadRefNum); return(err); }
- InPtr = InEnd = NULL;
-
- UpdateStatusWindowInput(myFSSPtr->name);
-
- reset_default_table();
- CurrentLine = ReadNextLine(); // Load first line of file into CurrentLine
- while (err == noErr && CurrentLine[0])
- {
- if (CurrentLine[0] == 'e' && // check for "end" marker
- CurrentLine[1] == 'n' &&
- CurrentLine[2] == 'd') reset_default_table();
-
- if (CurrentLine[0] == 't' &&
- CurrentLine[1] == 'a' &&
- CurrentLine[2] == 'b' &&
- CurrentLine[3] == 'l' &&
- CurrentLine[4] == 'e') read_translation_table();
-
- if (CurrentLine[0] == 'b' &&
- CurrentLine[1] == 'e' &&
- CurrentLine[2] == 'g' &&
- CurrentLine[3] == 'i' &&
- CurrentLine[4] == 'n' &&
- CurrentLine[5] == ' ')
- {
- err = DecodeFile(myFSSPtr, CurrentLine);
- if (IsError(Async_Write_PB.ioParam.ioResult)) break;
- reset_default_table();
- }
- else CurrentLine = ReadNextLine(); // Read next line
- }
- FSClose(ReadRefNum);
- while (Async_Close_PB.ioParam.ioResult == 1) continue; // Make sure final output close is complete
-
- // If we exited because of a write error, report it and delete the file
- if (Async_Write_PB.ioParam.ioResult)
- {
- ErrorAlert(kErrStringID, kDiskFullErr, Async_Write_PB.ioParam.ioResult);
- FSpDelete(&writeSpec);
- ParamText(writeSpec.name, NULL, NULL, NULL);
- (void)Alert(Alert_DiskError, NULL);
- }
- if (Async_Close_PB.ioParam.ioResult) ReportErrorAndDelete();
- return(err);
- }
-
- /*
- This routine gets called for any folder (or disk) that the caller wants
- processed as a set of component items, instead of as a single entity.
- The determining factor is the definition of the qWalkFolder compiler directive.
- */
- static OSErr ProcessFolder(FSSpecPtr myFSSPtr)
- {
- OSErr err = noErr;
- short index, oldIndex, localIndex;
- FSSpec localFSSpec, curFSSpec;
- CInfoPBRec cipb;
- Str255 fName, vFName;
- long dirID, origDirID;
- Boolean foundPosition;
-
- // copy the source locally to avoid recursion problems
- BlockMoveData(myFSSPtr, &localFSSpec, sizeof(FSSpec));
-
- // get the dirID for THIS folder, not it's parent!
- BlockMoveData(localFSSpec.name, fName, 32);
-
- cipb.hFileInfo.ioCompletion = 0L;
- cipb.hFileInfo.ioNamePtr = fName;
- cipb.hFileInfo.ioVRefNum = localFSSpec.vRefNum;
- cipb.hFileInfo.ioFDirIndex = 0; // use the dir & vRefNum;
- cipb.hFileInfo.ioDirID = localFSSpec.parID;
- err = PBGetCatInfoSync(&cipb);
-
- if (!err) {
- origDirID = cipb.dirInfo.ioDrDirID; // copy the sucker
- index = 1;
-
- // index through all contents of this folder
- while (err == noErr) {
- dirID = origDirID;
- localIndex = index;
- fName [0] = 0;
- cipb.hFileInfo.ioCompletion = 0L;
- cipb.hFileInfo.ioNamePtr = fName;
- cipb.hFileInfo.ioVRefNum = localFSSpec.vRefNum;
- cipb.hFileInfo.ioFDirIndex = localIndex; // use a real index
- cipb.hFileInfo.ioDirID = dirID;
- err = PBGetCatInfoSync(&cipb);
-
- if (!err) {
- BlockMoveData(fName, curFSSpec.name, 32);
- curFSSpec.vRefNum = cipb.hFileInfo.ioVRefNum;
- curFSSpec.parID = dirID;
-
- /*
- Check to see if this entry is a folder.
- */
- if (cipb.hFileInfo.ioFlAttrib & ioDirMask) {
- err = ProcessFolder(&curFSSpec);
- } else
- err = ProcessItem(&curFSSpec);
-
- /* If we've had an error, get out! */
- if (err) break;
-
- // dirID = origDirID;
- localIndex = index;
-
- /*
- Now take into account new files being created
- in the current directory & messing up our index.
- See Dev.CD Vol. XI:Tools & Apps (Moof!):Misc Utilities:
- Disinfectant & Source 2.5.1:Sample:Notes:Scan Alg
- */
- vFName [0] = 0;
- cipb.hFileInfo.ioCompletion = 0L;
- cipb.hFileInfo.ioNamePtr = vFName;
- cipb.hFileInfo.ioVRefNum = localFSSpec.vRefNum;
- cipb.hFileInfo.ioFDirIndex = localIndex; // use a real index
- cipb.hFileInfo.ioDirID = dirID;
- err = PBGetCatInfoSync(&cipb);
- oldIndex = index;
- if (!err) {
- /* If they're equal - same place, go to next */
- if (EqualString (vFName, fName, false, false))
- index++;
- }
-
- /* If we didn't advance, then perhaps a file was created or deleted */
- if (oldIndex == index) {
- oldIndex = index; /* save off the old */
- index = 0; /* and start at the beginning */
- err = noErr;
- vFName [0] = 0;
- foundPosition = false;
-
- while (!foundPosition) {
- index++;
- vFName [0] = 0;
- cipb.hFileInfo.ioCompletion = 0L;
- cipb.hFileInfo.ioNamePtr = vFName;
- cipb.hFileInfo.ioVRefNum = localFSSpec.vRefNum;
- cipb.hFileInfo.ioFDirIndex = index; /* now use a real index */
- cipb.hFileInfo.ioDirID = dirID;
- err = PBGetCatInfoSync(&cipb);
-
- if (err == fnfErr) { // we've just been deleted
- index = oldIndex;
- foundPosition = true;
- err = noErr; // have to remember to reset this!
- }
-
- /* found same file & same index position */
- /* so try the next item */
- if ((!foundPosition) && EqualString(fName, vFName, false, false)) {
- index++;
- foundPosition = true;
- }
- }
- }
- }
- }
- }
-
- return(err);
- }
-
- /*
- This routine is called when the user chooses "Select File…" from the
- File Menu.
-
- Currently it simply calls the new StandardGetFile routine to have the
- user select a single file (any type, numTypes = -1) and then calls the
- SendODOCToSelf routine in order to process it.
-
- The reason we send an odoc to ourselves is two fold: 1) it keeps the code
- cleaner as all file openings go through the same process, and 2) if events
- are ever recordable, the right things happen (this is called Factoring!)
-
- Modification of this routine to only select certain types of files, selection
- of multiple files, and/or handling of folder & disk selection is left
- as an exercise to the reader.
- */
- pascal void SelectFile(void)
- {
- StandardFileReply stdReply;
- SFTypeList theTypeList;
-
- StandardGetFile(NULL, -1, theTypeList, &stdReply);
- if (stdReply.sfGood) // user did not cancel
- SendODOCToSelf(&stdReply.sfFile); // so send me an event!
- }
-
- /*
- This routine is called during the program's initialization and gives you
- a chance to allocate or initialize any of your own globals that your
- dropbox needs.
-
- You return a boolean value which determines if you were successful.
- Returning false will cause DropShell to exit immediately.
- */
- pascal Boolean InitUserGlobals(void)
- {
- return(true); // nothing to do, it we must be successful!
- }
-
- /*
- This routine is called during the program's cleanup and gives you
- a chance to deallocate any of your own globals that you allocated
- in the above routine.
- */
- pascal void DisposeUserGlobals(void)
- {
- // nothing to do for our sample dropbox
- }
-